package me.zhengjie.modules.gis.service.impl;

import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.gis.domain.UrlAccessRecord;
import me.zhengjie.modules.gis.domain.UrlValidate;
import me.zhengjie.modules.gis.repository.UrlAccessRecordRepository;
import me.zhengjie.modules.gis.repository.UrlValidateRepository;
import me.zhengjie.modules.gis.service.dto.AccessRecordQueryCriteria;
import me.zhengjie.modules.gis.service.dto.UrlValidateResult;
import me.zhengjie.utils.DateUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QRCodeUtil;
import me.zhengjie.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.awt.image.BufferedImage;
import java.net.URLDecoder;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@RequiredArgsConstructor
public class RoleService {

    private static final Logger logger = LoggerFactory.getLogger(RoleService.class);

    private final UrlValidateRepository urlValidateRepository;
    private final UrlAccessRecordRepository accessRecordRepository;

    //获取二维码
    public BufferedImage retrieveQrImage(String url, String key, Integer day) {
        String newUrl = url + (url.contains("?") ? "&" : "?") + "vKey=" + key;
        BufferedImage image = null;
        try {
            image = QRCodeUtil.createQRCode(newUrl, false, true, false);
        } catch (Exception e) {
            logger.error("生成二维码失败，错误原因：" + e.getMessage());
        }
        if (image != null) {
            addValidate(url, key, day);
        }
        return image;
    }

    public void addValidate(String url, String key, Integer day) {
        if (day == null) {
            day = 0;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, day + 1);
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        UrlValidate validate = new UrlValidate();
        validate.setKey(key);
        validate.setUrl(removeParams(url));
        validate.setValid(new Date(calendar.getTimeInMillis()));
        urlValidateRepository.save(validate);
    }

    //验证url有效期
    public UrlValidateResult validateUrl(String url, String ip) {
        UrlValidateResult result = new UrlValidateResult();
        UrlAccessRecord record = new UrlAccessRecord();
        String vKey = getOneParameter(url, "vKey");
        String originalUrl = removeParams(url);
        record.setUrl(originalUrl);
        record.setKey(vKey);
        record.setIp(ip);
        record.setTime(new Timestamp(System.currentTimeMillis()));
        result.setSuccess(false);
        if (StringUtils.isBlank(vKey) || vKey.length() != 32) {
            record.setType(2);
            result.setType(2);
            result.setMessage("未注册的链接");
        } else {
            List<UrlValidate> validateList = urlValidateRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
                List<Predicate> res = new ArrayList<>();
                res.add(criteriaBuilder.equal(root.get("key"), vKey));
                res.add(criteriaBuilder.equal(root.get("url"), originalUrl));
                return criteriaQuery.where(res.toArray(new Predicate[0])).getRestriction();
            });
            if (validateList.isEmpty()) {
                record.setType(2);
                result.setType(2);
                result.setMessage("未注册的链接");
            } else {
                UrlValidate validate = validateList.get(0);
                if (validate.getValid().getTime() < System.currentTimeMillis()) {
                    record.setType(3);
                    result.setType(3);
                    result.setMessage("该链接已失效");
                } else {
                    record.setType(1);
                    result.setType(1);
                    result.setMessage("链接有效");
                    result.setSuccess(true);
                }
            }
        }
        accessRecordRepository.save(record);
        return result;
    }

    public static String getOneParameter(String url,String keyWord) {
        String retValue = "";
        try {
            final String charset = "utf-8";
            url = URLDecoder.decode(url, charset);
            if (url.indexOf('?') != -1) {
                final String contents = url.substring(url.indexOf('?') + 1);
                String[] keyValues = contents.split("&");
                for (int i = 0; i < keyValues.length; i++) {
                    String key = keyValues[i].substring(0, keyValues[i].indexOf("="));
                    String value = keyValues[i].substring(keyValues[i].indexOf("=") + 1);
                    if (key.equals(keyWord)) {
                        return value;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return retValue;
    }

    public static String removeParams(String url) {
        String result = url;
        try {
            final String charset = "utf-8";
            result = URLDecoder.decode(url, charset);
            if (result.startsWith("http:")) {
                result = result.substring(7);
            }
            if (result.startsWith("https:")) {
                result = result.substring(8);
            }
            if (result.indexOf('?') != -1) {
                result = result.substring(0, result.indexOf('?'));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return result;
        }
        return result;
    }

    public Map<String,Object> queryAccessRecords(AccessRecordQueryCriteria criteria, Pageable pageable) {
        Sort sort = Sort.by(Sort.Direction.DESC,"time");
        Pageable pageable1 = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
        Page<UrlAccessRecord> page = accessRecordRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> res = new ArrayList<>();
            if (StringUtils.isNotBlank(criteria.getStartDate())) {
                java.util.Date start = DateUtil.parseDate(criteria.getStartDate(), DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS);
                res.add(criteriaBuilder.greaterThanOrEqualTo(root.get("time"), new Timestamp(start.getTime())));
            }
            if (StringUtils.isNotBlank(criteria.getEndDate())) {
                java.util.Date end = DateUtil.parseDate(criteria.getEndDate(), DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS);
                res.add(criteriaBuilder.lessThanOrEqualTo(root.get("time"), new Timestamp(end.getTime())));
            }
            if (StringUtils.isNotBlank(criteria.getUrl())) {
                res.add(criteriaBuilder.like(root.get("url"), "%" + criteria.getUrl().trim() + "%"));
            }
            if (criteria.getType() != null) {
                res.add(criteriaBuilder.equal(root.get("type"), criteria.getType()));
            }
            return criteriaQuery.where(res.toArray(new Predicate[0])).getRestriction();
        }, pageable1);
        return PageUtil.toPage(page);
    }

    public Map<String,Object> queryUrlValidate(AccessRecordQueryCriteria criteria, Pageable pageable){
        Sort sort = Sort.by(Sort.Direction.DESC,"valid");
        Pageable pageable1 = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
        Page<UrlValidate> page = urlValidateRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> res = new ArrayList<>();
            if (StringUtils.isNotBlank(criteria.getStartDate())) {
                res.add(criteriaBuilder.greaterThanOrEqualTo(root.get("valid"), new Timestamp(0)));
            }
            if (StringUtils.isNotBlank(criteria.getEndDate())) {
                res.add(criteriaBuilder.lessThanOrEqualTo(root.get("valid"), new Timestamp(0)));
            }
            if (StringUtils.isNotBlank(criteria.getUrl())) {
                res.add(criteriaBuilder.like(root.get("url"), "%" + criteria.getUrl().trim() + "%"));
            }
            if (StringUtils.isNotBlank(criteria.getKey())) {
                res.add(criteriaBuilder.like(root.get("url"), "%" + criteria.getKey().trim() + "%"));
            }
            return criteriaQuery.where(res.toArray(new Predicate[0])).getRestriction();
        }, pageable1);
        for (UrlValidate validate: page) {
            validate.setValidFlag(validate.getValid() != null && validate.getValid().getTime() >= System.currentTimeMillis());
        }
        return PageUtil.toPage(page);
    }

    public void updateUrlValidDate(Integer id, String date) {
        Optional<UrlValidate> result = urlValidateRepository.findById(Long.valueOf(id));
        if (result.isPresent()) {
            UrlValidate validate = result.get();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            java.util.Date date1 = null;
            try {
                date1 = sdf.parse(date);
            } catch (ParseException ignored) {
            }
            if (date1 == null) return;
            validate.setValid(new Date(date1.getTime()));
            urlValidateRepository.save(validate);
        }
    }

    public void deleteUrlValidate(Integer id) {
        if (id != null) {
            urlValidateRepository.deleteById(Long.valueOf(id));
        }
    }

}
